package controller;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import model.CourseFormatOption;
import model.MoodleCourse;
import model.MoodleException;
import model.MoodleWarning;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import service.MoodleRestWSConfigSingleton;
import service.MoodleRestWSConnector;

/**
 * Класс с методами Веб-сервисов Moodle, работающих с курсами.
 *
 * @author UNI\kgp
 */
public class MoodleCourseWS {

    private final JSONParser parser = new JSONParser();

    /**
     * Universal method of creating courses.
     *
     * @param newCourses set of the courses that will be created
     * @return set of created cources identified by their id and shortname
     * @throws model.MoodleException if response of the {@code moodleWSRestCall}
     * method is Moodle exception
     * @throws org.json.simple.parser.ParseException while parsing of Moodle
     * JSON-response detects error
     *
     * @see MoodleRestWSConnector
     */
    public Set<MoodleCourse> createCourses(Set<MoodleCourse> newCourses) throws MoodleException, ParseException {

        MoodleRestWSConfigSingleton config = MoodleRestWSConfigSingleton.getInstance();

        String domainName = config.getDomainName();
        String token = config.getToken();
        String restformat = config.getRestFormat();
        //Web-service's function name
        String functionName = "core_course_create_courses";

        StringBuilder urlParameters = new StringBuilder();
        int i = 0;
        for (MoodleCourse course : newCourses) {
            try {
                urlParameters.append("&courses[").append(i).append("][fullname]=").append(URLEncoder.encode(course.getFullname(), "UTF-8"))
                        .append("&courses[").append(i).append("][shortname]=").append(URLEncoder.encode(course.getShortname(), "UTF-8"))
                        .append("&courses[").append(i).append("][categoryid]=").append(course.getCategoryid())
                        .append("&courses[").append(i).append("][idnumber]=").append(URLEncoder.encode(course.getIdnumber(), "UTF-8"))
                        .append("&courses[").append(i).append("][summary]=").append(URLEncoder.encode(course.getSummary(), "UTF-8"))
                        .append("&courses[").append(i).append("][summaryformat]=").append(course.getSummaryformat())
                        .append("&courses[").append(i).append("][format]=").append(URLEncoder.encode(course.getFormat(), "UTF-8"))
                        .append("&courses[").append(i).append("][showgrades]=").append(course.getShowgrades())
                        .append("&courses[").append(i).append("][newsitems]=").append(course.getNewsitems())
                        .append("&courses[").append(i).append("][startdate]=").append(course.getStartdate())
                        .append("&courses[").append(i).append("][numsections]=").append(course.getNumsections())
                        .append("&courses[").append(i).append("][maxbytes]=").append(course.getMaxbytes())
                        .append("&courses[").append(i).append("][showreports]=").append(course.getShowreports())
                        .append("&courses[").append(i).append("][visible]=").append(course.getVisible())
                        .append("&courses[").append(i).append("][hiddensections]=").append(course.getHiddensections())
                        .append("&courses[").append(i).append("][groupmode]=").append(course.getGroupmode())
                        .append("&courses[").append(i).append("][groupmodeforce]=").append(course.getGroupmodeforce())
                        .append("&courses[").append(i).append("][defaultgroupingid]=").append(course.getDefaultgroupingid())
                        .append("&courses[").append(i).append("][enablecompletion]=").append(course.getEnablecompletion())
                        .append("&courses[").append(i).append("][completionnotify]=").append(course.getCompletionnotify())
                        .append("&courses[").append(i).append("][lang]=").append(URLEncoder.encode(course.getLang(), "UTF-8"))
                        .append("&courses[").append(i).append("][forcetheme]=").append(URLEncoder.encode(course.getForcetheme(), "UTF-8"));
                int j = 0;
                for (CourseFormatOption formatOption : course.getCourseformatoptions()) {
                    urlParameters.append("&courses[").append(i).append("][courseformatoptions][").append(j).append("][name]=").append(URLEncoder.encode(formatOption.getName(), "UTF-8"))
                            .append("&courses[").append(i).append("][courseformatoptions][").append(j).append("][value]=").append(URLEncoder.encode(formatOption.getValue(), "UTF-8"));
                    j++;
                }
            } catch (UnsupportedEncodingException ex) {
                Logger.getLogger(MoodleCategoryWS.class
                        .getName()).log(Level.SEVERE, null, ex);
            }
            i++;
        }

        //Request to Web-service's function invoke
        String response = MoodleRestWSConnector.moodleWSRestCall(restformat, domainName, token, functionName, urlParameters.toString());

        Set<MoodleCourse> courses = new HashSet<MoodleCourse>();
        //Parsing of JSON-response
        Object obj = parser.parse(response);
        JSONArray array = (JSONArray) obj;
        //Fore some id it was empty array
        if (array.size() != 0) {
            for (Object arrayItem : array) {
                JSONObject jsonObj = (JSONObject) arrayItem;
                long id = (long) jsonObj.get("id");
                String shortname = jsonObj.get("shortname").toString();
                MoodleCourse course = new MoodleCourse(id, shortname);
                courses.add(course);
            }
        }

        return courses;
    }

    /**
     * Creates a single course (supplementary method).
     *
     * @param newCourse course to be created
     * @return created {@code MoodleCourse} instance
     * @throws model.MoodleException if {@code createCourses} method throws
     * {@code MoodleException}
     * @throws org.json.simple.parser.ParseException if {@code createCourses}
     * method throws {@code ParseException}
     */
    public MoodleCourse createCourse(MoodleCourse newCourse) throws MoodleException, ParseException {
        Set<MoodleCourse> newCourses = new HashSet<MoodleCourse>();
        newCourses.add(newCourse);
        Set<MoodleCourse> createdCourses = createCourses(newCourses);
        MoodleCourse createdCourse = createdCourses.iterator().next();
        return createdCourse;
    }

    /**
     * Universal method of getting of courses.
     *
     * @param ids List of course id. If empty return all courses except front
     * page course.
     * @return a set of courses in accordance with the list of id
     * @throws MoodleException
     * @throws ParseException
     */
    public Set<MoodleCourse> getCourses(Set<Long> ids) throws MoodleException, ParseException {

        MoodleRestWSConfigSingleton config = MoodleRestWSConfigSingleton.getInstance();

        String domainName = config.getDomainName();
        String token = config.getToken();
        String restformat = config.getRestFormat();
        //Web-service's function name
        String functionName = "core_course_get_courses";
        StringBuilder urlParameters = new StringBuilder();
        //Empty criteria makes return the full list of categories
        if (ids.isEmpty()) {
            urlParameters.append("");
        } else {
            int i = 0;
            for (long id : ids) {
                urlParameters.append("&options[ids][").append(i).append("]=").append(id);
                i++;
            }
        }

        //Request to Web-service's function invoke
        String response = MoodleRestWSConnector.moodleWSRestCall(restformat, domainName, token, functionName, urlParameters.toString());
        Set<MoodleCourse> courses = new HashSet<MoodleCourse>();
        //Parsing of JSON-response
        Object obj = parser.parse(response);
        JSONArray array = (JSONArray) obj;
        //Fore some id it was empty array
        if (array.size() != 0) {
            for (Object arrayItem : array) {
                JSONObject jsonObj = (JSONObject) arrayItem;
                long id = (long) jsonObj.get("id");
                String shortname = jsonObj.get("shortname").toString();
                long categoryid = (long) jsonObj.get("categoryid");
                long categorysortorder = (long) jsonObj.get("categorysortorder");
                String fullname = jsonObj.get("fullname").toString();
                String idnumber = jsonObj.get("idnumber").toString();
                String summary = jsonObj.get("summary").toString();
                byte summaryformat = ((Long) jsonObj.get("summaryformat")).byteValue();
                String format = jsonObj.get("format").toString();
                byte showgrades = ((Long) jsonObj.get("showgrades")).byteValue();
                int newsitems = ((Long) jsonObj.get("newsitems")).intValue();
                long startdate = (long) jsonObj.get("startdate");
                int numsections = ((Long) jsonObj.get("numsections")).intValue();
                long maxbytes = (long) jsonObj.get("maxbytes");
                byte showreports = ((Long) jsonObj.get("showreports")).byteValue();
                byte visible = ((Long) jsonObj.get("visible")).byteValue();
                int hiddensections = (jsonObj.get("hiddensections") == null) ? 0 : ((Long) jsonObj.get("hiddensections")).intValue();
                byte groupmode = ((Long) jsonObj.get("groupmode")).byteValue();
                byte groupmodeforce = ((Long) jsonObj.get("groupmodeforce")).byteValue();
                long defaultgroupingid = (long) jsonObj.get("defaultgroupingid");
                long timecreated = (long) jsonObj.get("timecreated");
                long timemodified = (long) jsonObj.get("timemodified");
                byte enablecompletion = ((Long) jsonObj.get("enablecompletion")).byteValue();
                byte completionnotify = ((Long) jsonObj.get("completionnotify")).byteValue();
                String lang = jsonObj.get("lang").toString();
                String forcetheme = jsonObj.get("forcetheme").toString();
                //Filling format options of the course
                List<CourseFormatOption> courseformatoptions = new ArrayList<CourseFormatOption>();
                JSONArray courseformatoptionlist = (JSONArray) jsonObj.get("courseformatoptions");
                Iterator<JSONObject> iterator = courseformatoptionlist.iterator();
                while (iterator.hasNext()) {
                    JSONObject formatoptionJSONObj = iterator.next();
                    String name = (String) formatoptionJSONObj.get("name");
                    //Despite to Moodle Web Services API docs some returned vaues are not String!!!
                    String value = (String) formatoptionJSONObj.get("value").toString();
                    CourseFormatOption formatOption = new CourseFormatOption(name, value);
                    courseformatoptions.add(formatOption);
                }
                MoodleCourse course = new MoodleCourse(id, shortname, categoryid, categorysortorder,
                        fullname, idnumber, summary, summaryformat, format, showgrades, newsitems,
                        startdate, numsections, maxbytes, showreports, visible, hiddensections,
                        groupmode, groupmodeforce, defaultgroupingid, timecreated, timemodified,
                        enablecompletion, completionnotify, lang, forcetheme, courseformatoptions);
                courses.add(course);
            }
        }

        return courses;
    }

    /**
     * Get all courses except front page course (supplementary method).
     *
     * @return total set of MoodleCourse instances.
     * @throws MoodleException
     * @throws ParseException
     */
    public Set<MoodleCourse> getAllCourses() throws MoodleException, ParseException {
        Set<Long> ids = new HashSet<Long>();
        Set<MoodleCourse> courses = getCourses(ids);
        return courses;
    }

    /**
     * Get a single course by course id (supplementary method).
     *
     * @param courseId course Id
     * @return MoodleCourse instance
     * @throws MoodleException
     * @throws ParseException
     */
    public MoodleCourse getCourseById(long courseId) throws MoodleException, ParseException {
        Set<Long> ids = new HashSet<Long>();
        ids.add(courseId);
        Set<MoodleCourse> courses = getCourses(ids);
        if (courses.size() != 1) {
            new MoodleException("Duplcate", "duplicate ids", "Duplicate name values!");
        }
        MoodleCourse course = courses.iterator().next();
        return course;
    }

    /**
     * Universal method for updating of courses.
     *
     * @param updatedCourses set of courses with updated properties
     * @return an optional set of MoodleWarnings
     * @throws MoodleException
     * @throws org.json.simple.parser.ParseException
     */
    public Set<MoodleWarning> updateCourses(Set<MoodleCourse> updatedCourses) throws MoodleException, ParseException {

        MoodleRestWSConfigSingleton config = MoodleRestWSConfigSingleton.getInstance();

        String domainName = config.getDomainName();
        String token = config.getToken();
        String restformat = config.getRestFormat();
        //Web-service's function name
        String functionName = "core_course_update_courses";

        StringBuilder urlParameters = new StringBuilder();
        int i = 0;
        for (MoodleCourse course : updatedCourses) {
            try {
                urlParameters.append("&courses[").append(i).append("][id]=").append(course.getId())
                        .append("&categories[").append(i).append("][fullname]=").append(URLEncoder.encode(course.getFullname(), "UTF-8"))
                        .append("&categories[").append(i).append("][shortname]=").append(URLEncoder.encode(course.getShortname(), "UTF-8"))
                        .append("&categories[").append(i).append("][categoryid]=").append(course.getCategoryid())
                        .append("&categories[").append(i).append("][idnumber]=").append(URLEncoder.encode(course.getIdnumber(), "UTF-8"))
                        .append("&categories[").append(i).append("][summary]=").append(URLEncoder.encode(course.getSummary(), "UTF-8"))
                        .append("&categories[").append(i).append("][summaryformat]=").append(course.getSummaryformat())
                        .append("&categories[").append(i).append("][format]=").append(URLEncoder.encode(course.getFormat(), "UTF-8"))
                        .append("&categories[").append(i).append("][showgrades]=").append(course.getShowgrades())
                        .append("&categories[").append(i).append("][newsitems]=").append(course.getNewsitems())
                        .append("&categories[").append(i).append("][startdate]=").append(course.getStartdate())
                        .append("&categories[").append(i).append("][showgrades]=").append(course.getShowgrades())
                        .append("&categories[").append(i).append("][numsections]=").append(course.getNumsections())
                        .append("&categories[").append(i).append("][maxbytes]=").append(course.getMaxbytes())
                        .append("&categories[").append(i).append("][showreports]=").append(course.getShowreports())
                        .append("&categories[").append(i).append("][visible]=").append(course.getVisible())
                        .append("&categories[").append(i).append("][hiddensections]=").append(course.getHiddensections())
                        .append("&categories[").append(i).append("][groupmode]=").append(course.getGroupmode())
                        .append("&categories[").append(i).append("][groupmodeforce]=").append(course.getGroupmodeforce())
                        .append("&categories[").append(i).append("][defaultgroupingid]=").append(course.getDefaultgroupingid())
                        .append("&categories[").append(i).append("][enablecompletion]=").append(course.getEnablecompletion())
                        .append("&categories[").append(i).append("][completionnotify]=").append(course.getCompletionnotify())
                        .append("&categories[").append(i).append("][lang]=").append(URLEncoder.encode(course.getLang(), "UTF-8"))
                        .append("&categories[").append(i).append("][forcetheme]=").append(URLEncoder.encode(course.getForcetheme(), "UTF-8"));
                int j = 0;
                for (CourseFormatOption formatOption : course.getCourseformatoptions()) {
                    urlParameters.append("&courses[").append(i).append("][courseformatoptions][").append(j).append("][name]=").append(URLEncoder.encode(formatOption.getName(), "UTF-8"))
                            .append("&courses[").append(i).append("][courseformatoptions][").append(j).append("][value]=").append(URLEncoder.encode(formatOption.getValue(), "UTF-8"));
                    j++;
                }
            } catch (UnsupportedEncodingException ex) {
                Logger.getLogger(MoodleCategoryWS.class
                        .getName()).log(Level.SEVERE, null, ex);
            }
            i++;
        }

        //Request to Web-service's function invoke - returns "null"
        String response = MoodleRestWSConnector.moodleWSRestCall(restformat, domainName, token, functionName, urlParameters.toString());
        Set<MoodleWarning> warnings = new HashSet<MoodleWarning>();
        //Parsing of JSON-response
        Object obj = parser.parse(response);
        JSONArray array = (JSONArray) obj;
        //Fore some id it was empty array
        if (array.size() != 0) {
            for (Object arrayItem : array) {
                JSONObject jsonObj = (JSONObject) arrayItem;
                long itemid = (long) jsonObj.get("itemid");
                String item = jsonObj.get("item").toString();
                String warningcode = jsonObj.get("warningcode").toString();
                String message = jsonObj.get("message").toString();
                MoodleWarning warning = new MoodleWarning(itemid, item, warningcode, message);
                warnings.add(warning);
            }
        }
        return warnings;
    }

    /**
     * Universal method of deleting courses.
     *
     * @param ids set of id of courses will be deleted
     * @throws MoodleException
     */
    public void deleteCourses(Set<Long> ids) throws MoodleException {

        MoodleRestWSConfigSingleton config = MoodleRestWSConfigSingleton.getInstance();

        String domainName = config.getDomainName();
        String token = config.getToken();
        String restformat = config.getRestFormat();
        //Web-service's function name
        String functionName = "core_course_delete_courses";

        StringBuilder urlParameters = new StringBuilder();
        int i = 0;
        for (Long id : ids) {
            urlParameters.append("&courseids[").append(i).append("]=").append(id);
            i++;
        }

        //Request to Web-service's function invoke - returns "null"
        String response = MoodleRestWSConnector.moodleWSRestCall(restformat, domainName, token, functionName, urlParameters.toString());
    }
}
